
import { queueWatcher } from "./asyncUpdateQueue.js";
import { Dep, popTarget, pushTarget } from "./dep.js";
// 标记 watcher
let uid = 0

/**
 * 
 * @param {*} cb 负责更新dom的回调函数
 * @param {*} options watcher 配置项
 * @param {*} vm Vue实例
 */
export function Watcher(cb, options = {}, vm = null) {
  // 标记每一个的watcher
  this.uid = uid++
  // 备份cb: 实例化的时候，new 了Watcher,返回了Watcher的一个实例，其内部有一个_cb的方法
  // 即： watch: {_cb: f()}
  this._cb = cb
  // 赋值, 此时的target为new出来的新对象
  // Dep.target = this
  // // 执行cb函数，cb函数中会发生vm.xx的属性读取，此时依赖收集
  // cb()
  // // 收集完成，随后释放target,确保每次new Watcher的时候，都能正确的收集到
  // Dep.target = null
  //
  //
  // computed
  // 回调函数执行的值
  this.value = null
  // computed 计算属性实现缓存原理，标记本次渲染周期已经被执行过
  this.dirty = !!options.lazy
  // vue实例
  this.vm = vm
  // 如果不是懒执行，直接执行cb函数，cb函数中会发生vm.xx的读取操作，从而进行依赖收集
  !options.lazy && this.get()
}
/**
 * 
 * 响应式更新数据，dep通知watcher执行update方法
 * update方法执行 this._cb 函数更新DOM
 */
Watcher.prototype.update = function () {
  // 通过Promise 将this._cb 的执行放到this.dirty = true 的后面
  // 否则第一次会无法执行
  // 因为this._cb 执行时，会更新组件，获取到的dirty还是上一次的
  // 所以这一步得放到异步更新里面 
  // Promise.resolve().then(() => {
  //   this._cb()
  // })

  /**
   * 
   */
  if(this.options.lazy) {
    // 懒执行（computed）
    // 当页面重新渲染时获取计算属性就可以执行 evalute 方法获取最新值了
    this.dirty = true
  } else {
    queueWatcher(this)
  }
  // 新一轮的渲染周期，设置dirty为true
  // this.dirty = true
}

/**
 * 负责执行 Watcher 的cb 函数
 * 执行时进行依赖收集
 */
Watcher.prototype.get = function() {
  pushTarget(this)
  this.value = this._cb.apply(this.vm)
  popTarget()
}

/**
 * 
 */
Watcher.prototype.evalute = function() {
  // 执行get, 触发_cb的执行
  this.get()
  // 将dirty设为false, 实现一个渲染周期内的computed缓存
  this.dirty = false
}

/**
 * 由刷新 watcher 队列的函数调用，负责执行watcher.get 方法
 */
Watcher.prototype.run = function() {
  this.get()
}